/****************************************************
	文件：UIManager.cs
	作者：HuskyT
	邮箱：1005240602@qq.com
	日期：2021/1/14 13:25:17
	功能：UI 管理器
*****************************************************/

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using HTFW.Consts;
using HTFW.Core;
using HTFW.Logger;
using HTFW.Utility;
using HTFW.Utility.Global;
using HTFW.Utility.Runtime;
using EventType = HTFW.Core.EventType;
using Object = UnityEngine.Object;

namespace HTFW.UI
{
    public class UIManager : Singleton<UIManager>, IManager, IViewUpdate
    {
        private const int ReferenceScreenWidth = 1080;
        private const int ReferenceScreenHeight = 1920;

        Dictionary<string, ViewBase> mShowingMap;
        Dictionary<string, ViewBase> mLoadedMap;
        Dictionary<UILayer, Transform> mUILayerMap;

        GameObject mUIRoot;
        Canvas mCanvas;
        CanvasScaler mCanvasScaler;
        Camera mUICamera;
        EventSystem mEventSystem;
        public GameObject UIRoot => mUIRoot;
        public Canvas Canvas => mCanvas;
        public CanvasScaler CanvasScaler => mCanvasScaler;
        public Camera UICamera => mUICamera;
        public EventSystem EventSystem => mEventSystem;

        /// <summary>
        /// 实机 高/宽
        /// </summary>
        public float AdaptRatio { get; private set; }

        /// <summary>
        /// 适配后的实机UI大小
        /// </summary>
        public Vector2 UISize { get; private set; }

        /// <summary>
        /// 开启/禁用 UI
        /// </summary>
        public bool Enable
        {
            set => mEventSystem.enabled = value;
        }

        public void Init(params object[] args)
        {
            mShowingMap = new Dictionary<string, ViewBase>();
            mLoadedMap = new Dictionary<string, ViewBase>();
            mUILayerMap = new Dictionary<UILayer, Transform>();

            AdaptRatio = 1.0f * Screen.height / Screen.width;

            //节点
            mUIRoot = new GameObject("[Global_UI_Root]");
            GameObject.DontDestroyOnLoad(mUIRoot);

            //相机
            GameObject cameraObj = new GameObject("UICamera");
            cameraObj.transform.SetParent(mUIRoot.transform, false);
            mUICamera = cameraObj.AddComponent<Camera>();
            mUICamera.clearFlags = CameraClearFlags.Depth;
            mUICamera.cullingMask = 1 << 5;
            mUICamera.orthographic = true;
            mUICamera.orthographicSize = Camera.main.orthographicSize;
            mUICamera.transform.localPosition = new Vector3(0, 0, -20);
            mUICamera.allowHDR = false;
            mUICamera.allowMSAA = false;
            mUICamera.useOcclusionCulling = false;

            //画布
            GameObject canvasObj = new GameObject("GlobalCanavs");
            canvasObj.transform.SetParent(mUIRoot.transform, false);
            mCanvas = canvasObj.AddComponent<Canvas>();
            mCanvas.renderMode = RenderMode.ScreenSpaceCamera;
            mCanvas.worldCamera = mUICamera;
            mCanvas.planeDistance = 10;
            mCanvas.sortingOrder = 20;

            //画布适配器
            mCanvasScaler = canvasObj.AddComponent<CanvasScaler>();
            mCanvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
            mCanvasScaler.referenceResolution = new Vector2(ReferenceScreenWidth, ReferenceScreenHeight);
            if (mCanvasScaler.matchWidthOrHeight == 0)
            {
                UISize = new Vector2(ReferenceScreenWidth, ReferenceScreenWidth * AdaptRatio);
            }

            canvasObj.AddComponent<GraphicRaycaster>();

            //层级
            foreach (UILayer uiLayer in Enum.GetValues(typeof(UILayer)))
            {
                GameObject obj = new GameObject(uiLayer.ToString());
                obj.transform.SetParent(canvasObj.transform, false);
                RectTransform rt = obj.AddComponent<RectTransform>();
                rt.anchorMin = Vector2.zero;
                rt.anchorMax = Vector2.one;
                rt.sizeDelta = Vector2.zero;
                Canvas canvas = obj.AddComponent<Canvas>();
                canvas.overrideSorting = true;
                canvas.sortingOrder = GetBaseSortingOrderByLayer(uiLayer);
                obj.AddComponent<GraphicRaycaster>();
                mUILayerMap.Add(uiLayer, obj.transform);
            }

            //事件系统
            GameObject eventObj = new GameObject("EventSystem");
            eventObj.transform.SetParent(mUIRoot.transform, false);
            mEventSystem = eventObj.AddComponent<EventSystem>();
            eventObj.AddComponent<StandaloneInputModule>();

            //设置层级
            RuntimeUtil.SetLayerRecursively(mUIRoot, LayerMask.NameToLayer("UI"));
        }

        public void Dispose()
        {
            CloseAllWindow();
            UnloadAllCacheWindow();
        }

        /// <summary>
        /// 获取 UI 对应层级父物体的 Transform
        /// </summary>
        /// <param name="uiLayer"></param>
        /// <returns></returns>
        public Transform GetUILayerTrans(UILayer uiLayer)
        {
            return mUILayerMap[uiLayer];
        }

        /// <summary>
        /// 获取对应 UI 层级的 SortingOrder
        /// </summary>
        /// <param name="uiLayer"></param>
        /// <returns></returns>
        public int GetBaseSortingOrderByLayer(UILayer uiLayer)
        {
            return (int) uiLayer * 100;
        }

        /// <summary>
        /// 显示UI窗口
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T ShowWindow<T>() where T : ViewBase
        {
            if (!AttributeService.BindPrefabCtrl.TryGetPath<T>(out string path))
            {
                HLog.Error($"尚未绑定UI类型与预制体路径，UIName：{typeof(T).ToString()}");
                return null;
            }

            if (mShowingMap.ContainsKey(path)) return (T) mShowingMap[path];
            if (mLoadedMap.TryGetValue(path, out ViewBase loadedView))
            {
                loadedView.Show();
                loadedView.Transform.SetAsLastSibling();
                mLoadedMap.Remove(path);
                mShowingMap.Add(path, loadedView);
                return (T) loadedView;
            }
            else
            {
                T view = Activator.CreateInstance<T>();
                view.InitSync(path, true);
                view.Show();
                view.Transform.SetAsLastSibling();
                mShowingMap.Add(path, view);
                return view;
            }
        }

        /// <summary>
        /// 关闭正在显示的 UI 窗口
        /// </summary>
        public void CloseWindow<T>() where T : ViewBase
        {
            if (!AttributeService.BindPrefabCtrl.TryGetPath<T>(out string path))
            {
                HLog.Error($"尚未绑定UI类型与预制体路径，UIName：{typeof(T).ToString()}");
                return;
            }

            CloseWindow(path);
        }

        /// <summary>
        /// 关闭正在显示的 UI 窗口
        /// </summary>
        /// <param name="path">UI 预制体路径</param>
        public void CloseWindow(string path)
        {
            if (mShowingMap.TryGetValue(path, out ViewBase showingView))
            {
                showingView.Hide();
                mShowingMap.Remove(path);
                if (showingView.UIType == UIType.Once) //关闭即销毁
                    showingView.Dispose();
                else if (showingView.UIType == UIType.Resident) //常驻内存
                    mLoadedMap.Add(path, showingView);
            }
        }

        /// <summary>
        /// 关闭除指定窗口以外的所有正在显示的 UI 窗口
        /// </summary>
        /// <param name="exceptViewType"></param>
        public void CloseAllWindow(Type exceptViewType = null)
        {
            string exceptViewPath = string.Empty;
            if (exceptViewType != null
                && !AttributeService.BindPrefabCtrl.TryGetPath(exceptViewType, out exceptViewPath))
            {
                HLog.Error($"尚未绑定UI类型与预制体路径，UIName：{exceptViewType.ToString()}");
                return;
            }

            List<string> toHideWndLst = new List<string>();
            foreach (var showing in mShowingMap)
            {
                if (showing.Key.Equals(exceptViewPath)) continue;

                toHideWndLst.Add(showing.Key);
                showing.Value.Hide();
                if (showing.Value.UIType == UIType.Once) //关闭即销毁
                    showing.Value.Dispose();
                else if (showing.Value.UIType == UIType.Resident) //常驻内存
                    mLoadedMap.Add(showing.Key, showing.Value);
            }

            foreach (var item in toHideWndLst)
                mShowingMap.Remove(item);
        }

        /// <summary>
        /// 预加载 UI 窗口（同步）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void PreloadWindowSync<T>() where T : ViewBase
        {
            if (!AttributeService.BindPrefabCtrl.TryGetPath<T>(out string path))
            {
                HLog.Error($"尚未绑定UI类型与预制体路径，UIName：{typeof(T).ToString()}");
                return;
            }

            if (mLoadedMap.ContainsKey(path)) return;
            if (new ViewBase() is T view)
            {
                view.InitSync(path, false);
                mLoadedMap.Add(path, view);
            }
        }

        /// <summary>
        /// 预加载 UI 窗口（异步）
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void PreloadWindowAsync<T>(Action onCompleted) where T : ViewBase
        {
            if (!AttributeService.BindPrefabCtrl.TryGetPath<T>(out string path))
            {
                HLog.Error($"尚未绑定UI类型与预制体路径，UIName：{typeof(T).ToString()}");
                return;
            }

            if (mLoadedMap.ContainsKey(path)) return;
            if (new ViewBase() is T view)
            {
                view.InitAsync(path, false, () =>
                {
                    mLoadedMap.Add(path, view);
                    onCompleted?.Invoke();
                });
            }
        }

        /// <summary>
        /// 获取 UI 窗口
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T GetWindow<T>() where T : ViewBase
        {
            if (!AttributeService.BindPrefabCtrl.TryGetPath<T>(out string path))
            {
                HLog.Error($"尚未绑定UI类型与预制体路径，UIName：{typeof(T).ToString()}");
                return null;
            }

            if (mShowingMap.TryGetValue(path, out ViewBase showingView))
            {
                return showingView as T;
            }

            if (mLoadedMap.TryGetValue(path, out ViewBase loadedView))
            {
                return loadedView as T;
            }

            return null;
        }

        /// <summary>
        /// 卸载预加载未显示的 UI 窗口
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void UnloadWindow<T>() where T : ViewBase
        {
            if (!AttributeService.BindPrefabCtrl.TryGetPath<T>(out string path))
            {
                HLog.Error($"尚未绑定UI类型与预制体路径，UIName：{typeof(T).ToString()}");
                return;
            }

            if (mLoadedMap.TryGetValue(path, out ViewBase loadedView))
            {
                loadedView.Dispose();
                mLoadedMap.Remove(path);
            }
        }

        /// <summary>
        /// 卸载所有预加载未显示的 UI 窗口
        /// </summary>
        public void UnloadAllCacheWindow()
        {
            foreach (var loaded in mLoadedMap)
            {
                loaded.Value.Dispose();
            }

            mLoadedMap.Clear();
        }

        /// <summary>
        /// 发送 UI 窗口消息
        /// </summary>
        /// <param name="msgType"></param>
        /// <param name="args"></param>
        /// <typeparam name="T"></typeparam>
        public void SendWindowMessage<T>(UIMessageType msgType, params object[] args) where T : ViewBase
        {
            T view = GetWindow<T>();
            view.OnWindowMessageReceive(msgType, args);
        }

        public void OnViewUpdate()
        {
            HTDriver.GlobalContext.EventContext.Publish(EventType.UI_OnViewUpdate);
        }
    }
}